package com.spa.application.service.massagist;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spa.application.command.wx.WXRefundCommand;
import com.spa.application.dto.PageResult;
import com.spa.application.dto.massagist.MassagistOrderDetailDTO;
import com.spa.application.dto.massagist.MassagistOrderListDTO;
import com.spa.application.query.massagist.MassagistOrderDetailQuery;
import com.spa.application.query.massagist.MassagistOrderQuery;
import com.spa.domain.entity.*;
import com.spa.domain.service.*;
import com.spa.infrastructure.common.KeyConstant;
import com.spa.infrastructure.enums.dict.massagist.MassagistStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderIsTransferEnum;
import com.spa.infrastructure.enums.dict.order.OrderOperStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderSubStatusEnum;
import com.spa.infrastructure.exception.SpaException;
import com.spa.infrastructure.mapper.OrderMapperStruct;
import com.spa.infrastructure.repository.condition.OrderInfoCondition;
import com.spa.infrastructure.repository.condition.OrderTransferCondition;
import com.spa.infrastructure.repository.po.OrderInfoPO;
import com.spa.infrastructure.repository.service.SysConfigurationRepositoryService;
import com.spa.infrastructure.service.mp.FlowEnum;
import com.spa.infrastructure.service.mp.Producer;
import com.spa.infrastructure.service.sms.ApplicationConfiguration;
import com.spa.infrastructure.service.sms.SmsProvider;
import com.spa.infrastructure.service.sms.huaxin.HuaXinUtil;
import com.spa.infrastructure.service.wx.WXPayServiceV3;
import com.spa.infrastructure.service.yunxin.VoiceSenderService;
import com.spa.infrastructure.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.rmi.ServerException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class MassagistOrderApplicationService {

    @Autowired
    private OrderService orderService;

    @Autowired
    private MassagistService massagistService;

    @Autowired
    private Producer producer;

    @Autowired
    private WXPayServiceV3 wxPayServiceV3;

    @Resource
    private TradeService tradeService;

    @Autowired
    private EnvUtil envUtil;

    @Autowired
    private OrderTransferService orderTransferService;

    @Autowired
    private ProfitService profitService;

    @Resource
    private RedisUtil redisUtil;

    @Autowired
    private ShopUserService shopUserService;

    @Autowired
    private ApplicationConfiguration smsConfig;

    @Autowired
    private VoiceSenderService voiceSenderService;

    @Resource
    private SmsProvider smsProvider;

    @Autowired
    private SysConfigurationRepositoryService sysConfigurationRepositoryService;

    public PageResult<MassagistOrderListDTO> queryMyOrderByStatus(MassagistOrderQuery query) {
        long massagistId = SessionUtil.getUser().getExtUserId();
        OrderInfoCondition orderCondition = OrderMapperStruct.INSTANCE.query2Condition(query, massagistId);
        //后端兼容性处理 后续看是否能进行优化
        if (orderCondition.getStatus() == 0) {
            orderCondition.setStatus(null);
        }
        orderCondition.setIsTransfer(OrderIsTransferEnum.$0.getCode());
        orderCondition.setNeStatus(OrderStatusEnum.$10.getCode());
        OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.base();
        plug.setTicket(true);
        plug.setEvaluate(true);
        Page<OrderInfo> orderInfoPage = orderService.pageByCondition(orderCondition, plug);
        return OrderMapperStruct.INSTANCE.entity2MassagistOrderListDTO4Page(orderInfoPage);
    }

    /**
     * 根据id查订单详情
     *
     * @param query id
     * @return 订单
     */
    public MassagistOrderDetailDTO detail(MassagistOrderDetailQuery query) {
        OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.base();
        plug.setTicket(true);
        plug.setEvaluate(true);
        OrderInfo orderInfo = orderService.getOrderInfoById(Long.parseLong(query.getOrderId()), plug);
        return OrderMapperStruct.INSTANCE.entity2MassagistOrderDetailDTO(orderInfo);
    }

    /**
     * 技师接单
     * @param orderId
     */
    public void receiveOrder(Long orderId) throws ServerException {
        Massagist massagistInfo = massagistService.getMassagistInfoByUserId();
        if (massagistInfo.getStatus().equals(MassagistStatusEnum.$10.getCode())) {
            throw new ServerException("休息中，不能操作订单");
        }
        /**
         * 1.验证技师状态是否为可接单
         * 2.验证订单状态是否为待接单及是否过期
         * 3.按照过期时间验证 超时未接单自动取消判断
         */
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setOrderId(orderId);
        condition.setMassagistId(massagistInfo.getId());
        condition.setStatus(OrderStatusEnum.$20.getCode());
        if (!orderService.existsByCondition(condition)) {
            throw new ServerException("不存在待接订单,刷新页面重试");
        }
        OrderTransferCondition orderTransferCondition = new OrderTransferCondition();
        orderTransferCondition.setOrderId(orderId);
        List<OrderTransfer> orderTransfers = orderTransferService.list4GroupByOrderLimitOne(orderTransferCondition);
        if (CollUtil.isNotEmpty(orderTransfers)) {
            OrderTransfer orderTransfer = orderTransfers.get(0);
            if (!Objects.equals(orderTransfer.getServeMassagistId(), massagistInfo.getId())) {
                throw new SpaException("接单失败，该订单超时未接已重新发起转单");
            }
        }
        /**
         * 1.修改订单状态为已接单,
         * 2.修改技师状态为服务中
         */
        if (!massagistInfo.getStatus().equals(MassagistStatusEnum.$30.getCode())) {
            massagistService.optionMyStatus(massagistInfo.getId(), MassagistStatusEnum.$30.getCode());
        }
        OrderInfoPO po = new OrderInfoPO();
        po.setId(orderId);
        po.setTakeTime(new Date());
        po.setOrderOperStatus(OrderOperStatusEnum.$10.getCode());
        po.setStatus(OrderStatusEnum.$30.getCode());
        orderService.updateOrder(po);
        redisUtil.delete(RedisKeyUtil.getAutoTransferKey(String.valueOf(orderId)));
        redisUtil.remove(RedisKeyUtil.getAutoTransferGlobalKey(), String.valueOf(orderId));
        producer.send(FlowEnum.$TAKE, orderId);
    }

    /**
     * 技师已出发
     * @param orderId
     */
    public void setOut(Long orderId) throws ServerException {
        Long userId = SessionUtil.getUser().getExtUserId();
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setOrderId(orderId);
        condition.setMassagistId(userId);
        condition.setStatus(OrderStatusEnum.$30.getCode());
        if (!orderService.existsByCondition(condition)) {
            throw new ServerException("不存在进行中的订单,刷新页面重试");
        }

        OrderInfoPO po = new OrderInfoPO();
        po.setId(orderId);
        po.setDepartTime(new Date());
        po.setOrderOperStatus(OrderOperStatusEnum.$20.getCode());
        orderService.updateOrder(po);
        producer.send(FlowEnum.$DEPART, orderId);
    }

    /**
     * 技师已到达
     * @param orderId
     */
    public void reach(Long orderId) throws ServerException {
        Long userId = SessionUtil.getUser().getExtUserId();
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setOrderId(orderId);
        condition.setMassagistId(userId);
        condition.setStatus(OrderStatusEnum.$30.getCode());
        if (!orderService.existsByCondition(condition)) {
            throw new ServerException("不存在进行中的订单,刷新页面重试");
        }

        OrderInfoPO po = new OrderInfoPO();
        po.setId(orderId);
        po.setArriveTime(new Date());
        po.setOrderOperStatus(OrderOperStatusEnum.$30.getCode());
        orderService.updateOrder(po);
        producer.send(FlowEnum.$ARRIVE, orderId);
    }

    /**
     * 技师开始服务
     * @param orderId
     */
    public void startService(Long orderId) throws ServerException {
        Long userId = SessionUtil.getUser().getExtUserId();
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setOrderId(orderId);
        condition.setMassagistId(userId);
        condition.setStatus(OrderStatusEnum.$30.getCode());
        if (!orderService.existsByCondition(condition)) {
            throw new ServerException("不存在进行中的订单,刷新页面重试");
        }

        OrderInfo orderInfo = orderService.getOrderInfoById(orderId, OrderService.OrderInfoPlug.empty());
        if (ObjectUtil.isNull(orderInfo.getExactLocation())) {
            throw new ServerException("请输入准确位置，精确到门牌号");
        }
        OrderInfoPO po = new OrderInfoPO();
        po.setId(orderId);
        po.setStartTime(new Date());
        po.setOrderOperStatus(OrderOperStatusEnum.$40.getCode());
        orderService.updateOrder(po);
    }

    /**
     * 技师完成服务
     * @param orderId
     */
    @Transactional
    public void completeService(Long orderId) throws ServerException {
        log.info("completeService {}", orderId);
        String lockKey = "massagist:cancelOrder：lock:" + orderId;
        String id = UUID.fastUUID().toString(true);
        try {
            boolean lockFlag = redisUtil.getLock(lockKey, id);
            if (!lockFlag) {
                throw new SpaException("操作频繁！");
            }

            Massagist massagistInfo = massagistService.getMassagistInfoByUserId();
            if (massagistInfo.getStatus().equals(MassagistStatusEnum.$10.getCode())) {
                throw new ServerException("休息中，不能操作订单");
            }

            /**
             *验证订单状态是否为进行中
             * 技师状态修改为可预约
             * 订单状态修改为已完成
             */
            OrderInfoCondition condition = new OrderInfoCondition();
            condition.setOrderId(orderId);
            condition.setMassagistId(massagistInfo.getId());
            condition.setStatus(OrderStatusEnum.$30.getCode());
            if (!orderService.existsByCondition(condition)) {
                throw new ServerException("不存在进行中订单,刷新页面重试");
            }
            optionMassagistStatus(massagistInfo.getId());

            //利润分润
            profitService.orderCompletion(orderId);

            OrderInfoPO po = new OrderInfoPO();
            po.setId(orderId);
            po.setEndTime(new Date());
            po.setOrderOperStatus(OrderOperStatusEnum.$50.getCode());
            po.setStatus(OrderStatusEnum.$40.getCode());
            orderService.updateOrder(po);

            producer.send(FlowEnum.$COMPLETE, orderId);
        } finally {
            redisUtil.releaseLock(lockKey, id);
        }
    }

    /**
     * 修改技师状态为可预约
     *
     * @author Ll
     * @date: 2024/2/18 10:54
     */
    public void optionMassagistStatus(Long massagistId) {
        massagistService.optionMyStatus(massagistId, MassagistStatusEnum.$20.getCode());
    }

    public void workplace(Long orderId, String workplace) {
        OrderInfo orderInfo = orderService.getOrderInfoById(orderId, OrderService.OrderInfoPlug.empty());
        if (orderInfo.getOrderOperStatus().equals(OrderOperStatusEnum.$30.getCode())) {
            OrderInfoPO orderInfoPo = new OrderInfoPO();
            orderInfoPo.setId(orderInfo.getId());
            orderInfoPo.setExactLocation(workplace);
            orderService.updateOrder(orderInfoPo);
        }
    }

    @Transactional
    public boolean cancelOrder(Long orderId) {
        String lockKey = "massagist:cancelOrder：lock:" + orderId;
        String id = UUID.fastUUID().toString(true);
        try {
            boolean lockFlag = redisUtil.getLock(lockKey, id);
            if (!lockFlag) {
                throw new SpaException("操作频繁！");
            }
            OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.base();
            plug.setTicket(true);
            plug.setEvaluate(true);
            OrderInfo orderInfo = orderService.getOrderInfoById(orderId, plug);
            LocalDateTime now = LocalDateTime.now();
            int minus = envUtil.isTest() ? 1 : 20;
            if (ObjectUtil.isNull(orderInfo.getArriveTime()) || now.minusMinutes(minus).isBefore(OrderUtils.toLocalDateTime(orderInfo.getArriveTime()))) {
                throw new SpaException("等待时间不足20分钟，不允许点击！");
            }
            orderInfo.setCancelReason("技师等待超过20分钟");
            orderInfo.setSubStatus(OrderSubStatusEnum.$30.getCode());
            boolean boo = orderService.cancelOrder(orderInfo);
            if (boo) {
                //此种情况也是正常进行 利润分成
                profitService.orderCompletion(orderId);
                this.optionMassagistStatus(orderInfo.getMassagistId());
                redisUtil.delete(RedisKeyUtil.getAutoTransferKey(String.valueOf(orderId)));
                redisUtil.remove(RedisKeyUtil.getAutoTransferGlobalKey(), String.valueOf(orderId));
                //短信通知下单人手机号
                smsProvider.sendNotice(orderInfo.getUserPhone(), HuaXinUtil.template4(orderInfo.getOrderCode()));
            }
            return boo;
        } finally {
            redisUtil.releaseLock(lockKey, id);
        }
    }

    /**
     * 进行退款
     *
     * @param source
     * @param refundPrice
     */
    private void refund(String source, int refundPrice, String orderCode) {
        Trade trade = tradeService.infoByOrderCode(orderCode);
        //退款
        WXRefundCommand refundCommand = new WXRefundCommand();
        refundCommand.setTotal(trade.getTotal());
        refundCommand.setRefund(refundPrice);
        refundCommand.setCurrency(trade.getCurrency());
        refundCommand.setOut_refund_no(trade.getTransactionId());
        refundCommand.setOut_trade_no(orderCode);
        try {
            wxPayServiceV3.refund(refundCommand);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void orderTransfer(Long orderId) {
        //转单是否存在
        if (orderTransferService.existsWaitByOrderId(orderId)) {
            throw new SpaException("转单失败，已存在此订单的待转单");
        }
        Massagist massagistInfo = massagistService.getMassagistInfoByUserId();
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setOrderId(orderId);
        condition.setMassagistId(massagistInfo.getId());
        condition.setStatus(OrderStatusEnum.$20.getCode());
        //订单是否未接单
        if (!orderService.existsByCondition(condition)) {
            throw new SpaException("转单失败，不存在待接单的订单");
        }
        OrderInfo orderInfo = orderService.getOrderInfoById(orderId, OrderService.OrderInfoPlug.empty());
        orderService.orderTransfer(orderInfo, massagistInfo.getId(), massagistInfo.getName());
        //主动甩的单子，不会再甩回来
        redisUtil.add(RedisKeyUtil.getAutoTransferKey(String.valueOf(orderInfo.getId())), orderInfo.getMassagistId() + "");
        //发送短信通知调度员
        List<String> list = new ArrayList<>();
        List<ShopUser> shopUsers = shopUserService.queryListByshopId(orderInfo.getShopId());
        if (CollUtil.isNotEmpty(shopUsers)) {
            shopUsers.stream().forEach(shopUser -> {
                if (StringUtils.isNotEmpty(shopUser.getOwnerPhone())) {
                    list.add(shopUser.getOwnerPhone());
                } else {
                    list.add(shopUser.getPhone());
                }
            });
        }
        // 短信通知
        smsProvider.sendNotice(list.stream().collect(Collectors.joining(",")), HuaXinUtil.template1(orderInfo.getOrderCode()));
        //语音通知
        if (smsConfig.isFlagSendVoiceSms()) {
            List<String> whiteList = sysConfigurationRepositoryService.getValueByKey(KeyConstant.SMS_WHITELIST);
            list.removeAll(whiteList);
            list.forEach(phone -> {
                LinkedHashMap<String, String> templateArgs = new LinkedHashMap<>();
                templateArgs.put("var1", orderInfo.getOrderCode());
                try {
                    voiceSenderService.send(phone, templateArgs, "402503");
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    public String callUser(long orderId) throws Exception {
        OrderInfo orderInfo = orderService.getOrderInfoById(orderId, OrderService.OrderInfoPlug.empty());
        //联系的要服务的用户
        return voiceSenderService.bind(orderInfo.getOrderAddress().getPhone(), orderInfo.getMassagistPhone());
    }
}
